// Copyright 2013 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_ARM64_MACRO_ASSEMBLER_ARM64_INL_H_
#define V8_ARM64_MACRO_ASSEMBLER_ARM64_INL_H_

#include <ctype.h>

#include "src/globals.h"

#include "src/arm64/assembler-arm64-inl.h"
#include "src/arm64/assembler-arm64.h"
#include "src/arm64/instrument-arm64.h"
#include "src/base/bits.h"
#include "src/macro-assembler.h"

namespace v8 {
namespace internal {

    MemOperand FieldMemOperand(Register object, int offset)
    {
        return MemOperand(object, offset - kHeapObjectTag);
    }

    void TurboAssembler::And(const Register& rd, const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        LogicalMacro(rd, rn, operand, AND);
    }

    void TurboAssembler::Ands(const Register& rd, const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        LogicalMacro(rd, rn, operand, ANDS);
    }

    void TurboAssembler::Tst(const Register& rn, const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        LogicalMacro(AppropriateZeroRegFor(rn), rn, operand, ANDS);
    }

    void TurboAssembler::Bic(const Register& rd, const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        LogicalMacro(rd, rn, operand, BIC);
    }

    void MacroAssembler::Bics(const Register& rd,
        const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        LogicalMacro(rd, rn, operand, BICS);
    }

    void TurboAssembler::Orr(const Register& rd, const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        LogicalMacro(rd, rn, operand, ORR);
    }

    void TurboAssembler::Orn(const Register& rd, const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        LogicalMacro(rd, rn, operand, ORN);
    }

    void TurboAssembler::Eor(const Register& rd, const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        LogicalMacro(rd, rn, operand, EOR);
    }

    void TurboAssembler::Eon(const Register& rd, const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        LogicalMacro(rd, rn, operand, EON);
    }

    void TurboAssembler::Ccmp(const Register& rn, const Operand& operand,
        StatusFlags nzcv, Condition cond)
    {
        DCHECK(allow_macro_instructions());
        if (operand.IsImmediate() && (operand.ImmediateValue() < 0)) {
            ConditionalCompareMacro(rn, -operand.ImmediateValue(), nzcv, cond, CCMN);
        } else {
            ConditionalCompareMacro(rn, operand, nzcv, cond, CCMP);
        }
    }

    void MacroAssembler::Ccmn(const Register& rn,
        const Operand& operand,
        StatusFlags nzcv,
        Condition cond)
    {
        DCHECK(allow_macro_instructions());
        if (operand.IsImmediate() && (operand.ImmediateValue() < 0)) {
            ConditionalCompareMacro(rn, -operand.ImmediateValue(), nzcv, cond, CCMP);
        } else {
            ConditionalCompareMacro(rn, operand, nzcv, cond, CCMN);
        }
    }

    void TurboAssembler::Add(const Register& rd, const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        if (operand.IsImmediate() && (operand.ImmediateValue() < 0) && IsImmAddSub(-operand.ImmediateValue())) {
            AddSubMacro(rd, rn, -operand.ImmediateValue(), LeaveFlags, SUB);
        } else {
            AddSubMacro(rd, rn, operand, LeaveFlags, ADD);
        }
    }

    void TurboAssembler::Adds(const Register& rd, const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        if (operand.IsImmediate() && (operand.ImmediateValue() < 0) && IsImmAddSub(-operand.ImmediateValue())) {
            AddSubMacro(rd, rn, -operand.ImmediateValue(), SetFlags, SUB);
        } else {
            AddSubMacro(rd, rn, operand, SetFlags, ADD);
        }
    }

    void TurboAssembler::Sub(const Register& rd, const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        if (operand.IsImmediate() && (operand.ImmediateValue() < 0) && IsImmAddSub(-operand.ImmediateValue())) {
            AddSubMacro(rd, rn, -operand.ImmediateValue(), LeaveFlags, ADD);
        } else {
            AddSubMacro(rd, rn, operand, LeaveFlags, SUB);
        }
    }

    void TurboAssembler::Subs(const Register& rd, const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        if (operand.IsImmediate() && (operand.ImmediateValue() < 0) && IsImmAddSub(-operand.ImmediateValue())) {
            AddSubMacro(rd, rn, -operand.ImmediateValue(), SetFlags, ADD);
        } else {
            AddSubMacro(rd, rn, operand, SetFlags, SUB);
        }
    }

    void TurboAssembler::Cmn(const Register& rn, const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        Adds(AppropriateZeroRegFor(rn), rn, operand);
    }

    void TurboAssembler::Cmp(const Register& rn, const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        Subs(AppropriateZeroRegFor(rn), rn, operand);
    }

    void TurboAssembler::Neg(const Register& rd, const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        if (operand.IsImmediate()) {
            Mov(rd, -operand.ImmediateValue());
        } else {
            Sub(rd, AppropriateZeroRegFor(rd), operand);
        }
    }

    void TurboAssembler::Negs(const Register& rd, const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        Subs(rd, AppropriateZeroRegFor(rd), operand);
    }

    void TurboAssembler::Adc(const Register& rd, const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        AddSubWithCarryMacro(rd, rn, operand, LeaveFlags, ADC);
    }

    void MacroAssembler::Adcs(const Register& rd,
        const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        AddSubWithCarryMacro(rd, rn, operand, SetFlags, ADC);
    }

    void MacroAssembler::Sbc(const Register& rd,
        const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        AddSubWithCarryMacro(rd, rn, operand, LeaveFlags, SBC);
    }

    void MacroAssembler::Sbcs(const Register& rd,
        const Register& rn,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        AddSubWithCarryMacro(rd, rn, operand, SetFlags, SBC);
    }

    void MacroAssembler::Ngc(const Register& rd,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        Register zr = AppropriateZeroRegFor(rd);
        Sbc(rd, zr, operand);
    }

    void MacroAssembler::Ngcs(const Register& rd,
        const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        Register zr = AppropriateZeroRegFor(rd);
        Sbcs(rd, zr, operand);
    }

    void TurboAssembler::Mvn(const Register& rd, uint64_t imm)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        Mov(rd, ~imm);
    }

#define DEFINE_FUNCTION(FN, REGTYPE, REG, OP)                          \
    void TurboAssembler::FN(const REGTYPE REG, const MemOperand& addr) \
    {                                                                  \
        DCHECK(allow_macro_instructions());                            \
        LoadStoreMacro(REG, addr, OP);                                 \
    }
    LS_MACRO_LIST(DEFINE_FUNCTION)
#undef DEFINE_FUNCTION

#define DEFINE_FUNCTION(FN, REGTYPE, REG, REG2, OP)                \
    void TurboAssembler::FN(const REGTYPE REG, const REGTYPE REG2, \
        const MemOperand& addr)                                    \
    {                                                              \
        DCHECK(allow_macro_instructions());                        \
        LoadStorePairMacro(REG, REG2, addr, OP);                   \
    }
    LSPAIR_MACRO_LIST(DEFINE_FUNCTION)
#undef DEFINE_FUNCTION

#define DECLARE_FUNCTION(FN, OP)                                    \
    void TurboAssembler::FN(const Register& rt, const Register& rn) \
    {                                                               \
        DCHECK(allow_macro_instructions());                         \
        OP(rt, rn);                                                 \
    }
    LDA_STL_MACRO_LIST(DECLARE_FUNCTION)
#undef DECLARE_FUNCTION

#define DECLARE_FUNCTION(FN, OP)                                    \
    void MacroAssembler::FN(const Register& rs, const Register& rt, \
        const Register& rn)                                         \
    {                                                               \
        DCHECK(allow_macro_instructions());                         \
        OP(rs, rt, rn);                                             \
    }
    STLX_MACRO_LIST(DECLARE_FUNCTION)
#undef DECLARE_FUNCTION

    void TurboAssembler::Asr(const Register& rd, const Register& rn,
        unsigned shift)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        asr(rd, rn, shift);
    }

    void TurboAssembler::Asr(const Register& rd, const Register& rn,
        const Register& rm)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        asrv(rd, rn, rm);
    }

    void TurboAssembler::B(Label* label)
    {
        DCHECK(allow_macro_instructions());
        b(label);
        CheckVeneerPool(false, false);
    }

    void TurboAssembler::B(Condition cond, Label* label)
    {
        DCHECK(allow_macro_instructions());
        B(label, cond);
    }

    void TurboAssembler::Bfi(const Register& rd, const Register& rn, unsigned lsb,
        unsigned width)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        bfi(rd, rn, lsb, width);
    }

    void MacroAssembler::Bfxil(const Register& rd,
        const Register& rn,
        unsigned lsb,
        unsigned width)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        bfxil(rd, rn, lsb, width);
    }

    void TurboAssembler::Bind(Label* label)
    {
        DCHECK(allow_macro_instructions());
        bind(label);
    }

    void TurboAssembler::Bl(Label* label)
    {
        DCHECK(allow_macro_instructions());
        bl(label);
    }

    void TurboAssembler::Blr(const Register& xn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!xn.IsZero());
        blr(xn);
    }

    void TurboAssembler::Br(const Register& xn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!xn.IsZero());
        br(xn);
    }

    void TurboAssembler::Brk(int code)
    {
        DCHECK(allow_macro_instructions());
        brk(code);
    }

    void MacroAssembler::Cinc(const Register& rd,
        const Register& rn,
        Condition cond)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        DCHECK((cond != al) && (cond != nv));
        cinc(rd, rn, cond);
    }

    void MacroAssembler::Cinv(const Register& rd,
        const Register& rn,
        Condition cond)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        DCHECK((cond != al) && (cond != nv));
        cinv(rd, rn, cond);
    }

    void TurboAssembler::Cls(const Register& rd, const Register& rn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        cls(rd, rn);
    }

    void TurboAssembler::Clz(const Register& rd, const Register& rn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        clz(rd, rn);
    }

    void TurboAssembler::Cneg(const Register& rd, const Register& rn,
        Condition cond)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        DCHECK((cond != al) && (cond != nv));
        cneg(rd, rn, cond);
    }

    // Conditionally zero the destination register. Only X registers are supported
    // due to the truncation side-effect when used on W registers.
    void MacroAssembler::CzeroX(const Register& rd,
        Condition cond)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsSP() && rd.Is64Bits());
        DCHECK((cond != al) && (cond != nv));
        csel(rd, xzr, rd, cond);
    }

    // Conditionally move a value into the destination register. Only X registers
    // are supported due to the truncation side-effect when used on W registers.
    void TurboAssembler::CmovX(const Register& rd, const Register& rn,
        Condition cond)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsSP());
        DCHECK(rd.Is64Bits() && rn.Is64Bits());
        DCHECK((cond != al) && (cond != nv));
        if (!rd.is(rn)) {
            csel(rd, rn, rd, cond);
        }
    }

    void TurboAssembler::Csdb()
    {
        DCHECK(allow_macro_instructions());
        csdb();
    }

    void TurboAssembler::Cset(const Register& rd, Condition cond)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        DCHECK((cond != al) && (cond != nv));
        cset(rd, cond);
    }

    void TurboAssembler::Csetm(const Register& rd, Condition cond)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        DCHECK((cond != al) && (cond != nv));
        csetm(rd, cond);
    }

    void TurboAssembler::Csinc(const Register& rd, const Register& rn,
        const Register& rm, Condition cond)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        DCHECK((cond != al) && (cond != nv));
        csinc(rd, rn, rm, cond);
    }

    void MacroAssembler::Csinv(const Register& rd,
        const Register& rn,
        const Register& rm,
        Condition cond)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        DCHECK((cond != al) && (cond != nv));
        csinv(rd, rn, rm, cond);
    }

    void MacroAssembler::Csneg(const Register& rd,
        const Register& rn,
        const Register& rm,
        Condition cond)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        DCHECK((cond != al) && (cond != nv));
        csneg(rd, rn, rm, cond);
    }

    void TurboAssembler::Dmb(BarrierDomain domain, BarrierType type)
    {
        DCHECK(allow_macro_instructions());
        dmb(domain, type);
    }

    void TurboAssembler::Dsb(BarrierDomain domain, BarrierType type)
    {
        DCHECK(allow_macro_instructions());
        dsb(domain, type);
    }

    void TurboAssembler::Debug(const char* message, uint32_t code, Instr params)
    {
        DCHECK(allow_macro_instructions());
        debug(message, code, params);
    }

    void MacroAssembler::Extr(const Register& rd,
        const Register& rn,
        const Register& rm,
        unsigned lsb)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        extr(rd, rn, rm, lsb);
    }

    void TurboAssembler::Fabs(const VRegister& fd, const VRegister& fn)
    {
        DCHECK(allow_macro_instructions());
        fabs(fd, fn);
    }

    void TurboAssembler::Fadd(const VRegister& fd, const VRegister& fn,
        const VRegister& fm)
    {
        DCHECK(allow_macro_instructions());
        fadd(fd, fn, fm);
    }

    void TurboAssembler::Fccmp(const VRegister& fn, const VRegister& fm,
        StatusFlags nzcv, Condition cond)
    {
        DCHECK(allow_macro_instructions());
        DCHECK((cond != al) && (cond != nv));
        fccmp(fn, fm, nzcv, cond);
    }

    void TurboAssembler::Fcmp(const VRegister& fn, const VRegister& fm)
    {
        DCHECK(allow_macro_instructions());
        fcmp(fn, fm);
    }

    void TurboAssembler::Fcmp(const VRegister& fn, double value)
    {
        DCHECK(allow_macro_instructions());
        if (value != 0.0) {
            UseScratchRegisterScope temps(this);
            VRegister tmp = temps.AcquireSameSizeAs(fn);
            Fmov(tmp, value);
            fcmp(fn, tmp);
        } else {
            fcmp(fn, value);
        }
    }

    void MacroAssembler::Fcsel(const VRegister& fd, const VRegister& fn,
        const VRegister& fm, Condition cond)
    {
        DCHECK(allow_macro_instructions());
        DCHECK((cond != al) && (cond != nv));
        fcsel(fd, fn, fm, cond);
    }

    void TurboAssembler::Fcvt(const VRegister& fd, const VRegister& fn)
    {
        DCHECK(allow_macro_instructions());
        fcvt(fd, fn);
    }

    void TurboAssembler::Fcvtas(const Register& rd, const VRegister& fn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        fcvtas(rd, fn);
    }

    void TurboAssembler::Fcvtau(const Register& rd, const VRegister& fn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        fcvtau(rd, fn);
    }

    void TurboAssembler::Fcvtms(const Register& rd, const VRegister& fn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        fcvtms(rd, fn);
    }

    void TurboAssembler::Fcvtmu(const Register& rd, const VRegister& fn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        fcvtmu(rd, fn);
    }

    void TurboAssembler::Fcvtns(const Register& rd, const VRegister& fn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        fcvtns(rd, fn);
    }

    void TurboAssembler::Fcvtnu(const Register& rd, const VRegister& fn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        fcvtnu(rd, fn);
    }

    void TurboAssembler::Fcvtzs(const Register& rd, const VRegister& fn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        fcvtzs(rd, fn);
    }
    void TurboAssembler::Fcvtzu(const Register& rd, const VRegister& fn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        fcvtzu(rd, fn);
    }

    void TurboAssembler::Fdiv(const VRegister& fd, const VRegister& fn,
        const VRegister& fm)
    {
        DCHECK(allow_macro_instructions());
        fdiv(fd, fn, fm);
    }

    void MacroAssembler::Fmadd(const VRegister& fd, const VRegister& fn,
        const VRegister& fm, const VRegister& fa)
    {
        DCHECK(allow_macro_instructions());
        fmadd(fd, fn, fm, fa);
    }

    void TurboAssembler::Fmax(const VRegister& fd, const VRegister& fn,
        const VRegister& fm)
    {
        DCHECK(allow_macro_instructions());
        fmax(fd, fn, fm);
    }

    void MacroAssembler::Fmaxnm(const VRegister& fd, const VRegister& fn,
        const VRegister& fm)
    {
        DCHECK(allow_macro_instructions());
        fmaxnm(fd, fn, fm);
    }

    void TurboAssembler::Fmin(const VRegister& fd, const VRegister& fn,
        const VRegister& fm)
    {
        DCHECK(allow_macro_instructions());
        fmin(fd, fn, fm);
    }

    void MacroAssembler::Fminnm(const VRegister& fd, const VRegister& fn,
        const VRegister& fm)
    {
        DCHECK(allow_macro_instructions());
        fminnm(fd, fn, fm);
    }

    void TurboAssembler::Fmov(VRegister fd, VRegister fn)
    {
        DCHECK(allow_macro_instructions());
        // Only emit an instruction if fd and fn are different, and they are both D
        // registers. fmov(s0, s0) is not a no-op because it clears the top word of
        // d0. Technically, fmov(d0, d0) is not a no-op either because it clears the
        // top of q0, but VRegister does not currently support Q registers.
        if (!fd.Is(fn) || !fd.Is64Bits()) {
            fmov(fd, fn);
        }
    }

    void TurboAssembler::Fmov(VRegister fd, Register rn)
    {
        DCHECK(allow_macro_instructions());
        fmov(fd, rn);
    }

    void TurboAssembler::Fmov(VRegister vd, double imm)
    {
        DCHECK(allow_macro_instructions());

        if (vd.Is1S() || vd.Is2S() || vd.Is4S()) {
            Fmov(vd, static_cast<float>(imm));
            return;
        }

        DCHECK(vd.Is1D() || vd.Is2D());
        if (IsImmFP64(imm)) {
            fmov(vd, imm);
        } else {
            uint64_t bits = bit_cast<uint64_t>(imm);
            if (vd.IsScalar()) {
                if (bits == 0) {
                    fmov(vd, xzr);
                } else {
                    UseScratchRegisterScope temps(this);
                    Register tmp = temps.AcquireX();
                    Mov(tmp, bits);
                    fmov(vd, tmp);
                }
            } else {
                Movi(vd, bits);
            }
        }
    }

    void TurboAssembler::Fmov(VRegister vd, float imm)
    {
        DCHECK(allow_macro_instructions());
        if (vd.Is1D() || vd.Is2D()) {
            Fmov(vd, static_cast<double>(imm));
            return;
        }

        DCHECK(vd.Is1S() || vd.Is2S() || vd.Is4S());
        if (IsImmFP32(imm)) {
            fmov(vd, imm);
        } else {
            uint32_t bits = bit_cast<uint32_t>(imm);
            if (vd.IsScalar()) {
                if (bits == 0) {
                    fmov(vd, wzr);
                } else {
                    UseScratchRegisterScope temps(this);
                    Register tmp = temps.AcquireW();
                    Mov(tmp, bit_cast<uint32_t>(imm));
                    Fmov(vd, tmp);
                }
            } else {
                Movi(vd, bits);
            }
        }
    }

    void TurboAssembler::Fmov(Register rd, VRegister fn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        fmov(rd, fn);
    }

    void MacroAssembler::Fmsub(const VRegister& fd, const VRegister& fn,
        const VRegister& fm, const VRegister& fa)
    {
        DCHECK(allow_macro_instructions());
        fmsub(fd, fn, fm, fa);
    }

    void TurboAssembler::Fmul(const VRegister& fd, const VRegister& fn,
        const VRegister& fm)
    {
        DCHECK(allow_macro_instructions());
        fmul(fd, fn, fm);
    }

    void MacroAssembler::Fnmadd(const VRegister& fd, const VRegister& fn,
        const VRegister& fm, const VRegister& fa)
    {
        DCHECK(allow_macro_instructions());
        fnmadd(fd, fn, fm, fa);
    }

    void MacroAssembler::Fnmsub(const VRegister& fd, const VRegister& fn,
        const VRegister& fm, const VRegister& fa)
    {
        DCHECK(allow_macro_instructions());
        fnmsub(fd, fn, fm, fa);
    }

    void TurboAssembler::Fsub(const VRegister& fd, const VRegister& fn,
        const VRegister& fm)
    {
        DCHECK(allow_macro_instructions());
        fsub(fd, fn, fm);
    }

    void MacroAssembler::Hint(SystemHint code)
    {
        DCHECK(allow_macro_instructions());
        hint(code);
    }

    void MacroAssembler::Hlt(int code)
    {
        DCHECK(allow_macro_instructions());
        hlt(code);
    }

    void TurboAssembler::Isb()
    {
        DCHECK(allow_macro_instructions());
        isb();
    }

    void TurboAssembler::Ldr(const CPURegister& rt, const Operand& operand)
    {
        DCHECK(allow_macro_instructions());
        ldr(rt, operand);
    }

    void TurboAssembler::Lsl(const Register& rd, const Register& rn,
        unsigned shift)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        lsl(rd, rn, shift);
    }

    void TurboAssembler::Lsl(const Register& rd, const Register& rn,
        const Register& rm)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        lslv(rd, rn, rm);
    }

    void TurboAssembler::Lsr(const Register& rd, const Register& rn,
        unsigned shift)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        lsr(rd, rn, shift);
    }

    void TurboAssembler::Lsr(const Register& rd, const Register& rn,
        const Register& rm)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        lsrv(rd, rn, rm);
    }

    void TurboAssembler::Madd(const Register& rd, const Register& rn,
        const Register& rm, const Register& ra)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        madd(rd, rn, rm, ra);
    }

    void TurboAssembler::Mneg(const Register& rd, const Register& rn,
        const Register& rm)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        mneg(rd, rn, rm);
    }

    void MacroAssembler::Movk(const Register& rd, uint64_t imm, int shift)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        movk(rd, imm, shift);
    }

    void TurboAssembler::Mrs(const Register& rt, SystemRegister sysreg)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rt.IsZero());
        mrs(rt, sysreg);
    }

    void MacroAssembler::Msr(SystemRegister sysreg, const Register& rt)
    {
        DCHECK(allow_macro_instructions());
        msr(sysreg, rt);
    }

    void TurboAssembler::Msub(const Register& rd, const Register& rn,
        const Register& rm, const Register& ra)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        msub(rd, rn, rm, ra);
    }

    void TurboAssembler::Mul(const Register& rd, const Register& rn,
        const Register& rm)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        mul(rd, rn, rm);
    }

    void TurboAssembler::Rbit(const Register& rd, const Register& rn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        rbit(rd, rn);
    }

    void TurboAssembler::Rev(const Register& rd, const Register& rn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        rev(rd, rn);
    }

    void TurboAssembler::Ret(const Register& xn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!xn.IsZero());
        ret(xn);
        CheckVeneerPool(false, false);
    }

    void MacroAssembler::Rev(const Register& rd, const Register& rn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        rev(rd, rn);
    }

    void TurboAssembler::Rev16(const Register& rd, const Register& rn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        rev16(rd, rn);
    }

    void TurboAssembler::Rev32(const Register& rd, const Register& rn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        rev32(rd, rn);
    }

    void TurboAssembler::Ror(const Register& rd, const Register& rs,
        unsigned shift)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        ror(rd, rs, shift);
    }

    void TurboAssembler::Ror(const Register& rd, const Register& rn,
        const Register& rm)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        rorv(rd, rn, rm);
    }

    void MacroAssembler::Sbfiz(const Register& rd,
        const Register& rn,
        unsigned lsb,
        unsigned width)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        sbfiz(rd, rn, lsb, width);
    }

    void TurboAssembler::Sbfx(const Register& rd, const Register& rn, unsigned lsb,
        unsigned width)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        sbfx(rd, rn, lsb, width);
    }

    void TurboAssembler::Scvtf(const VRegister& fd, const Register& rn,
        unsigned fbits)
    {
        DCHECK(allow_macro_instructions());
        scvtf(fd, rn, fbits);
    }

    void TurboAssembler::Sdiv(const Register& rd, const Register& rn,
        const Register& rm)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        sdiv(rd, rn, rm);
    }

    void MacroAssembler::Smaddl(const Register& rd,
        const Register& rn,
        const Register& rm,
        const Register& ra)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        smaddl(rd, rn, rm, ra);
    }

    void MacroAssembler::Smsubl(const Register& rd,
        const Register& rn,
        const Register& rm,
        const Register& ra)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        smsubl(rd, rn, rm, ra);
    }

    void TurboAssembler::Smull(const Register& rd, const Register& rn,
        const Register& rm)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        smull(rd, rn, rm);
    }

    void MacroAssembler::Smulh(const Register& rd,
        const Register& rn,
        const Register& rm)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        smulh(rd, rn, rm);
    }

    void TurboAssembler::Umull(const Register& rd, const Register& rn,
        const Register& rm)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        umaddl(rd, rn, rm, xzr);
    }

    void TurboAssembler::Sxtb(const Register& rd, const Register& rn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        sxtb(rd, rn);
    }

    void TurboAssembler::Sxth(const Register& rd, const Register& rn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        sxth(rd, rn);
    }

    void TurboAssembler::Sxtw(const Register& rd, const Register& rn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        sxtw(rd, rn);
    }

    void TurboAssembler::Ubfiz(const Register& rd, const Register& rn, unsigned lsb,
        unsigned width)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        ubfiz(rd, rn, lsb, width);
    }

    void TurboAssembler::Ubfx(const Register& rd, const Register& rn, unsigned lsb,
        unsigned width)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        ubfx(rd, rn, lsb, width);
    }

    void TurboAssembler::Ucvtf(const VRegister& fd, const Register& rn,
        unsigned fbits)
    {
        DCHECK(allow_macro_instructions());
        ucvtf(fd, rn, fbits);
    }

    void TurboAssembler::Udiv(const Register& rd, const Register& rn,
        const Register& rm)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        udiv(rd, rn, rm);
    }

    void MacroAssembler::Umaddl(const Register& rd,
        const Register& rn,
        const Register& rm,
        const Register& ra)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        umaddl(rd, rn, rm, ra);
    }

    void MacroAssembler::Umsubl(const Register& rd,
        const Register& rn,
        const Register& rm,
        const Register& ra)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        umsubl(rd, rn, rm, ra);
    }

    void TurboAssembler::Uxtb(const Register& rd, const Register& rn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        uxtb(rd, rn);
    }

    void TurboAssembler::Uxth(const Register& rd, const Register& rn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        uxth(rd, rn);
    }

    void TurboAssembler::Uxtw(const Register& rd, const Register& rn)
    {
        DCHECK(allow_macro_instructions());
        DCHECK(!rd.IsZero());
        uxtw(rd, rn);
    }

    void TurboAssembler::InitializeRootRegister()
    {
        ExternalReference isolate_root = ExternalReference::isolate_root(isolate());
        Mov(kRootRegister, Operand(isolate_root));
    }

    void MacroAssembler::SmiTag(Register dst, Register src)
    {
        DCHECK(dst.Is64Bits() && src.Is64Bits());
        DCHECK(SmiValuesAre32Bits() || SmiValuesAre31Bits());
        Lsl(dst, src, kSmiShift);
    }

    void MacroAssembler::SmiTag(Register smi) { SmiTag(smi, smi); }

    void TurboAssembler::SmiUntag(Register dst, Register src)
    {
        DCHECK(dst.Is64Bits() && src.Is64Bits());
        if (FLAG_enable_slow_asserts) {
            AssertSmi(src);
        }
        DCHECK(SmiValuesAre32Bits() || SmiValuesAre31Bits());
        Asr(dst, src, kSmiShift);
    }

    void TurboAssembler::SmiUntag(Register dst, const MemOperand& src)
    {
        DCHECK(dst.Is64Bits());
        if (SmiValuesAre32Bits()) {
            if (src.IsImmediateOffset() && src.shift_amount() == 0) {
                // Load value directly from the upper half-word.
                // Assumes that Smis are shifted by 32 bits and little endianness.
                DCHECK_EQ(kSmiShift, 32);
                Ldrsw(dst,
                    MemOperand(src.base(), src.offset() + (kSmiShift / kBitsPerByte),
                        src.addrmode()));

            } else {
                Ldr(dst, src);
                SmiUntag(dst);
            }
        } else {
            DCHECK(SmiValuesAre31Bits());
#ifdef V8_COMPRESS_POINTERS
            Ldrsw(dst, src);
#else
            Ldr(dst, src);
#endif
            SmiUntag(dst);
        }
    }

    void TurboAssembler::SmiUntag(Register smi) { SmiUntag(smi, smi); }

    void TurboAssembler::JumpIfSmi(Register value, Label* smi_label,
        Label* not_smi_label)
    {
        STATIC_ASSERT((kSmiTagSize == 1) && (kSmiTag == 0));
        // Check if the tag bit is set.
        if (smi_label) {
            Tbz(value, 0, smi_label);
            if (not_smi_label) {
                B(not_smi_label);
            }
        } else {
            DCHECK(not_smi_label);
            Tbnz(value, 0, not_smi_label);
        }
    }

    void TurboAssembler::JumpIfEqual(Register x, int32_t y, Label* dest)
    {
        Cmp(x, y);
        B(eq, dest);
    }

    void TurboAssembler::JumpIfLessThan(Register x, int32_t y, Label* dest)
    {
        Cmp(x, y);
        B(lt, dest);
    }

    void MacroAssembler::JumpIfNotSmi(Register value, Label* not_smi_label)
    {
        JumpIfSmi(value, nullptr, not_smi_label);
    }

    void MacroAssembler::JumpIfBothSmi(Register value1,
        Register value2,
        Label* both_smi_label,
        Label* not_smi_label)
    {
        STATIC_ASSERT((kSmiTagSize == 1) && (kSmiTag == 0));
        UseScratchRegisterScope temps(this);
        Register tmp = temps.AcquireX();
        // Check if both tag bits are clear.
        Orr(tmp, value1, value2);
        JumpIfSmi(tmp, both_smi_label, not_smi_label);
    }

    void MacroAssembler::JumpIfEitherSmi(Register value1,
        Register value2,
        Label* either_smi_label,
        Label* not_smi_label)
    {
        STATIC_ASSERT((kSmiTagSize == 1) && (kSmiTag == 0));
        UseScratchRegisterScope temps(this);
        Register tmp = temps.AcquireX();
        // Check if either tag bit is clear.
        And(tmp, value1, value2);
        JumpIfSmi(tmp, either_smi_label, not_smi_label);
    }

    void MacroAssembler::JumpIfEitherNotSmi(Register value1,
        Register value2,
        Label* not_smi_label)
    {
        JumpIfBothSmi(value1, value2, nullptr, not_smi_label);
    }

    void MacroAssembler::JumpIfBothNotSmi(Register value1,
        Register value2,
        Label* not_smi_label)
    {
        JumpIfEitherSmi(value1, value2, nullptr, not_smi_label);
    }

    void MacroAssembler::ObjectTag(Register tagged_obj, Register obj)
    {
        STATIC_ASSERT(kHeapObjectTag == 1);
        if (emit_debug_code()) {
            Label ok;
            Tbz(obj, 0, &ok);
            Abort(AbortReason::kObjectTagged);
            Bind(&ok);
        }
        Orr(tagged_obj, obj, kHeapObjectTag);
    }

    void MacroAssembler::ObjectUntag(Register untagged_obj, Register obj)
    {
        STATIC_ASSERT(kHeapObjectTag == 1);
        if (emit_debug_code()) {
            Label ok;
            Tbnz(obj, 0, &ok);
            Abort(AbortReason::kObjectNotTagged);
            Bind(&ok);
        }
        Bic(untagged_obj, obj, kHeapObjectTag);
    }

    void TurboAssembler::jmp(Label* L) { B(L); }

    void TurboAssembler::Push(Handle<HeapObject> handle)
    {
        UseScratchRegisterScope temps(this);
        Register tmp = temps.AcquireX();
        Mov(tmp, Operand(handle));
        // This is only used in test-heap.cc, for generating code that is not
        // executed. Push a padding slot together with the handle here, to
        // satisfy the alignment requirement.
        Push(padreg, tmp);
    }

    void TurboAssembler::Push(Smi smi)
    {
        UseScratchRegisterScope temps(this);
        Register tmp = temps.AcquireX();
        Mov(tmp, Operand(smi));
        Push(tmp);
    }

    void TurboAssembler::Claim(int64_t count, uint64_t unit_size)
    {
        DCHECK_GE(count, 0);
        uint64_t size = count * unit_size;

        if (size == 0) {
            return;
        }
        DCHECK_EQ(size % 16, 0);

        Sub(sp, sp, size);
    }

    void TurboAssembler::Claim(const Register& count, uint64_t unit_size)
    {
        if (unit_size == 0)
            return;
        DCHECK(base::bits::IsPowerOfTwo(unit_size));

        const int shift = CountTrailingZeros(unit_size, kXRegSizeInBits);
        const Operand size(count, LSL, shift);

        if (size.IsZero()) {
            return;
        }
        AssertPositiveOrZero(count);

        Sub(sp, sp, size);
    }

    void MacroAssembler::ClaimBySMI(const Register& count_smi, uint64_t unit_size)
    {
        DCHECK(unit_size == 0 || base::bits::IsPowerOfTwo(unit_size));
        const int shift = CountTrailingZeros(unit_size, kXRegSizeInBits) - kSmiShift;
        const Operand size(count_smi,
            (shift >= 0) ? (LSL) : (LSR),
            (shift >= 0) ? (shift) : (-shift));

        if (size.IsZero()) {
            return;
        }

        Sub(sp, sp, size);
    }

    void TurboAssembler::Drop(int64_t count, uint64_t unit_size)
    {
        DCHECK_GE(count, 0);
        uint64_t size = count * unit_size;

        if (size == 0) {
            return;
        }

        Add(sp, sp, size);
        DCHECK_EQ(size % 16, 0);
    }

    void TurboAssembler::Drop(const Register& count, uint64_t unit_size)
    {
        if (unit_size == 0)
            return;
        DCHECK(base::bits::IsPowerOfTwo(unit_size));

        const int shift = CountTrailingZeros(unit_size, kXRegSizeInBits);
        const Operand size(count, LSL, shift);

        if (size.IsZero()) {
            return;
        }

        AssertPositiveOrZero(count);
        Add(sp, sp, size);
    }

    void TurboAssembler::DropArguments(const Register& count,
        ArgumentsCountMode mode)
    {
        int extra_slots = 1; // Padding slot.
        if (mode == kCountExcludesReceiver) {
            // Add a slot for the receiver.
            ++extra_slots;
        }
        UseScratchRegisterScope temps(this);
        Register tmp = temps.AcquireX();
        Add(tmp, count, extra_slots);
        Bic(tmp, tmp, 1);
        Drop(tmp, kXRegSize);
    }

    void TurboAssembler::DropArguments(int64_t count, ArgumentsCountMode mode)
    {
        if (mode == kCountExcludesReceiver) {
            // Add a slot for the receiver.
            ++count;
        }
        Drop(RoundUp(count, 2), kXRegSize);
    }

    void TurboAssembler::DropSlots(int64_t count)
    {
        Drop(RoundUp(count, 2), kXRegSize);
    }

    void TurboAssembler::PushArgument(const Register& arg) { Push(padreg, arg); }

    void MacroAssembler::DropBySMI(const Register& count_smi, uint64_t unit_size)
    {
        DCHECK(unit_size == 0 || base::bits::IsPowerOfTwo(unit_size));
        const int shift = CountTrailingZeros(unit_size, kXRegSizeInBits) - kSmiShift;
        const Operand size(count_smi,
            (shift >= 0) ? (LSL) : (LSR),
            (shift >= 0) ? (shift) : (-shift));

        if (size.IsZero()) {
            return;
        }

        Add(sp, sp, size);
    }

    void MacroAssembler::CompareAndBranch(const Register& lhs,
        const Operand& rhs,
        Condition cond,
        Label* label)
    {
        if (rhs.IsImmediate() && (rhs.ImmediateValue() == 0) && ((cond == eq) || (cond == ne))) {
            if (cond == eq) {
                Cbz(lhs, label);
            } else {
                Cbnz(lhs, label);
            }
        } else {
            Cmp(lhs, rhs);
            B(cond, label);
        }
    }

    void TurboAssembler::TestAndBranchIfAnySet(const Register& reg,
        const uint64_t bit_pattern,
        Label* label)
    {
        int bits = reg.SizeInBits();
        DCHECK_GT(CountSetBits(bit_pattern, bits), 0);
        if (CountSetBits(bit_pattern, bits) == 1) {
            Tbnz(reg, MaskToBit(bit_pattern), label);
        } else {
            Tst(reg, bit_pattern);
            B(ne, label);
        }
    }

    void TurboAssembler::TestAndBranchIfAllClear(const Register& reg,
        const uint64_t bit_pattern,
        Label* label)
    {
        int bits = reg.SizeInBits();
        DCHECK_GT(CountSetBits(bit_pattern, bits), 0);
        if (CountSetBits(bit_pattern, bits) == 1) {
            Tbz(reg, MaskToBit(bit_pattern), label);
        } else {
            Tst(reg, bit_pattern);
            B(eq, label);
        }
    }

    void MacroAssembler::InlineData(uint64_t data)
    {
        DCHECK(is_uint16(data));
        InstructionAccurateScope scope(this, 1);
        movz(xzr, data);
    }

    void MacroAssembler::EnableInstrumentation()
    {
        InstructionAccurateScope scope(this, 1);
        movn(xzr, InstrumentStateEnable);
    }

    void MacroAssembler::DisableInstrumentation()
    {
        InstructionAccurateScope scope(this, 1);
        movn(xzr, InstrumentStateDisable);
    }

    void MacroAssembler::AnnotateInstrumentation(const char* marker_name)
    {
        DCHECK_EQ(strlen(marker_name), 2);

        // We allow only printable characters in the marker names. Unprintable
        // characters are reserved for controlling features of the instrumentation.
        DCHECK(isprint(marker_name[0]) && isprint(marker_name[1]));

        InstructionAccurateScope scope(this, 1);
        movn(xzr, (marker_name[1] << 8) | marker_name[0]);
    }

} // namespace internal
} // namespace v8

#endif // V8_ARM64_MACRO_ASSEMBLER_ARM64_INL_H_
